﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Text;
using System.Windows.Forms;
using BGSpaceShooter;

namespace BGSpaceShooterServer
{
    internal class Router
    {
        private ServerState serverState = ServerState.NotInitialized;
        protected static Router instance;
        private Timer timer;

        public ServerState ServerState
        {
            get
            {
                return serverState;
            }
            set
            {
                if ((value == ServerState.NotInitialized && serverState != ServerState.NotInitialized) ||
                    (value == ServerState.Initialization && serverState != ServerState.NotInitialized && serverState != ServerState.Initialization) ||
                    (value == ServerState.Game && serverState == ServerState.Exiting))
                {
                    throw new InvalidAsynchronousStateException("Invalid state change!");
                }
                if (value == ServerState.Game && serverState != value)
                {
                    StartGame();
                }
                serverState = value;
                NetworkController.ChangeServerState(serverState, PlayerController.GetActivePlayers());
            }
        }

        protected Router()
        {
            timer = new Timer();
            timer.Interval = 20;
            timer.Tick += new EventHandler(timer_Tick);
        }

        void timer_Tick(object sender, EventArgs e)
        {
            Update();
        }

        public void Update()
        {
            if (ServerState == ServerState.Game)
            {
                ProjectileController.DisposeOldProjectiles(); //To mozna raz na sekunde odpalac

                NetworkController.SendEvents(PlayerController.GetActivePlayers(),
                                             ProjectileController.CreateProjectilesForPlayers(
                                                 PlayerController.GetActivePlayers()), EventType.Creation);
                NetworkController.SendEvents(PlayerController.GetActivePlayers(),ProjectileController.GetProjectileCreationStatus(ProjectileController.PrepareDebrisForPalyer(PlayerController.GetActivePlayers())),EventType.Creation);

                ProjectileController.UpdateProjectiles();
                
                GameStateController.Update(PlayerController.GetActivePlayers(),

                                           ProjectileController.GetActiveProjectiles());
                NetworkController.SendEvents(PlayerController.GetActivePlayers(), PlayerController.CheckIfPlayersAreDead(),
                             EventType.ChatMessage);


                NetworkController.SendUpdates(PlayerController.GetActivePlayers(), PlayerController.GetUpdates());

                NetworkController.SendEvents(PlayerController.GetActivePlayers(), PlayerController.GetHPUpdates(),
                                             EventType.HPUpdate);

                NetworkController.SendEvents(PlayerController.GetActivePlayers(),
                                             ProjectileController.GetProjectileDestructionStatus(
                                                 ProjectileController.GetZombieProjectiles()), EventType.Destruction);

                NetworkController.SendEvents(PlayerController.GetActivePlayers(), PlayerController.KickRetiredPlayers(), EventType.Destruction);
            }
        }

        public static Router StartRouter(int maxPlayers, int port)
        {
            if (instance == null)
            {
                instance = new Router();
                instance.Initialize(maxPlayers, port);
            }
            else
            {
                instance.timer.Stop();
                instance.ServerState = ServerState.Exiting;
                while (!instance.NetworkController.ConnectionClosed)
                {

                }
                instance = new Router();
                instance.Initialize(maxPlayers, port);
            }
            instance.timer.Start();

            return instance;
        }

        public static Router Instance
        {
            get
            {
                if (instance != null)
                    return instance;
                else
                {
                    throw new Exception("Router not initialized!");
                }
            }
        }

        private void Initialize(int maxPlayers, int port)
        {
            NetworkController = new NetworkController(port);
            PlayerController = new PlayerController(maxPlayers);
            ProjectileController = new ProjectileController();
            GameStateController = new GameStateController();

            NetworkController.MessageRecieved += new CTSEventHandler(NetworkController_MessageRecieved);
        }

        /// <summary>
        /// Handler wiadomości przychodzących
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="eventArgs"></param>
        void NetworkController_MessageRecieved(object sender, CTSEventArgs eventArgs)
        {

            switch (eventArgs.ClientToServerPacket.MessageType)
            {
                case MessageType.RequestJoin:
                    if (ServerState == ServerState.Initialization)
                    {
                        int newPlayerID = PlayerController.RegisterNewPlayer(eventArgs.ClientToServerPacket,
                                                                             eventArgs.Sender);
                        if (newPlayerID < 0)
                        {
                            NetworkController.SendDenyJoinMessage(eventArgs.Sender);
                        }
                        else
                        {
                            NetworkController.SendAcceptJoinMessage(eventArgs.Sender, newPlayerID);
                        }
                    }
                    else
                    {
                        NetworkController.SendDenyJoinMessage(eventArgs.Sender);
                    }
                    break;
                case MessageType.Leave:
                    PlayerController.RemovePlayer(eventArgs.ClientToServerPacket.PlayerID);
                    break;
                case MessageType.RequestUpdate:
                    GameStateController.UpdatePlayerInput(
                        PlayerController.GetPlayerById(eventArgs.ClientToServerPacket.PlayerID),
                        eventArgs.ClientToServerPacket.KeysPressed);
                    PlayerController.GetPlayerById(eventArgs.ClientToServerPacket.PlayerID).LastRequest = DateTime.Now;
                    break;
                case MessageType.ChatText:
                    NetworkController.SendEvents(PlayerController.GetActivePlayers(),
                                                 new List<ServerStatusResponse>
                                                     {
                                                         new ServerStatusResponse()
                                                             {Message = eventArgs.ClientToServerPacket.Message}
                                                     },
                                                 EventType.ChatMessage);
                    break;
            }
        }

        private void StartGame()
        {
            foreach (Player player in PlayerController.GetActivePlayers())
            {
                NetworkController.SendStartGameInitialization(new IPEndPoint(player.PlayerAddress, player.PlayerPort), PlayerController.GetActivePlayers());
                player.LastRequest = DateTime.Now;
            }

            foreach (Player player in PlayerController.GetActivePlayers())
            {
                NetworkController.SendEvents(new List<Player> { player },
                           new List<ServerStatusResponse> { new ServerStatusResponse() { Message = "Twoje Id: " + player.PlayerID } },
                           EventType.ChatMessage);
            }
            //Przygotowanie asteroid
            NetworkController.SendEvents(PlayerController.GetActivePlayers(), ProjectileController.GetProjectileCreationStatus(ProjectileController.PrepareDebris(170)), EventType.Creation);

        }

        public NetworkController NetworkController { get; set; }

        public ProjectileController ProjectileController { get; set; }

        public PlayerController PlayerController
        {
            get;
            set;
        }

        public GameStateController GameStateController
        {
            get;
            set;
        }
    }
}
